#include <iostream>
#include<memory>
#include<boost/asio.hpp>
#include<set>
using boost::asio::ip::tcp;
const int max_length = 1024;
typedef std::shared_ptr<tcp::socket> socket_ptr;            //别名
std::set<std::shared_ptr<std::thread>> thread_set;  

//会话，收发数据
void session(socket_ptr sock) {
    try {
        for (;;) {
            char data[max_length];
            std::memset(data, '\0', max_length);    //初始化
            boost::system::error_code error;

            //服务器不能保证一次读完，所以用read_some
            size_t length = sock->read_some(boost::asio::buffer(data, max_length), error);

            //错误类型为对端关闭
            if (error == boost::asio::error::eof) {
                std::cout << "connection closed by peer" << std::endl;
                break;
            }
            else if (error) {
                throw boost::system::system_error(error);
            }
            //输出对端地址
            std::cout << "receive from " << sock->remote_endpoint().address().to_string() << std::endl;
            std::cout << "receive message is " << data << std::endl;
            
            boost::asio::write(*sock, boost::asio::buffer(data, length));//回传给对方
        }
        
    }
    catch (std::exception &e) {
        std::cerr << "Exception in thread: " << e.what() << "\n" << std::endl;

    }
}

//接收客户端连接
void server(boost::asio::io_context &io_context,unsigned short port) {
    tcp::acceptor a(io_context, tcp::endpoint(tcp::v4(), port));//服务器用来接收客户端的连接的
    for (;;) {
        socket_ptr socket(new tcp::socket(io_context));
        a.accept(*socket);    //有对端的连接过来就用accept接收
        
        //session(socket);由于是同步的会话，会阻塞主线程工作，得使用线程
        //创建子线程，独立运行,不阻塞主线程，第一个参数为函数，第二个为第一个函数的参数
        auto t = std::make_shared<std::thread>(session,socket);
        //利用智能指针的引用技术规则，让局部t智能指针引用计数加1，不会在局部被释放掉。
        thread_set.insert(t);
    }
}
int main()
{
    try{
        boost::asio::io_context ioc;
        server(ioc, 10086);
        for (auto t:thread_set) {
            t->join();//主线程会等子线程执行完成
        }
    }
    catch (std::exception &e) {
        std::cout << "Exception is" << e.what()<< std::endl;
    }
    std::cout << "Hello World!\n";
    return 0;
}

